<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div>
    <h2>一、typeof</h2>
  </div>
  <script>
    typeof 1 // 'number'
    typeof '1' // 'string'
    typeof undefined // 'undefined'
    typeof true // 'boolean'
    typeof Symbol() // 'symbol'
    typeof null // 'object'
    typeof [] // 'object'
    typeof {} // 'object'
    typeof console // 'object'
    typeof console.log // 'function'
    // 引用类型数据，用typeof来判断的话，除了function会被识别出来之外，其余的都输出object
    // 如果我们想要判断一个变量是否存在，可以使用typeof：(不能使用if(a)， 若a未声明，则报错)
  </script>
  <div>
    <h2>二、instanceof</h2>
  </div>
  <script>
    // instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
    // 构造函数通过new可以实例对象，instanceof能判断这个对象是否是之前那个构造函数生成的对象
    // object instanceof constructor
    // 定义构建函数
    let Car = function() {}
    let benz = new Car()
    benz instanceof Car // true
    let car = new String('xxx')
    car instanceof String // true
    let str = 'xxx'
    str instanceof String // false
    // 判断数据类型
    // 1.typeof()
    // 2.
    console.log([] instanceof Array);
    console.log({} instanceof Array);
    console.log([] instanceof Object);
    console.log({} instanceof Object);
    // 3.Object.prototype.toString() // 不能判断自定义类型，如实例化函数
    console.log([].__prototype__ === Array.prototype);
    console.log([].__prototype__ === Array.prototype);
    // console.log([].__prototype__.__prototype__ === Array.prototype);

    // 4.Array.isArray() // 返回布尔值



    // 关于instanceof的实现原理，可以参考下面：
    function myInstanceof(left, right) {
        // 这里先用typeof来判断基础数据类型，如果是，直接返回false
        if(typeof left !== 'object' || left === null) return false;
        // getProtypeOf是Object对象自带的API，能够拿到参数的原型对象
        let proto = Object.getPrototypeOf(left);
        while(true) {                  
            if(proto === null) return false;
            if(proto === right.prototype) return true;//找到相同原型对象，返回true
            proto = Object.getPrototypeof(proto);
        }
    }


    // 三、区别
    // typeof与instanceof都是判断数据类型的方法，区别如下：
    // typeof会返回一个变量的基本类型，instanceof返回的是一个布尔值
    // instanceof 可以准确地判断复杂引用数据类型，但是不能正确判断基础数据类型
    // 而typeof 也存在弊端，它虽然可以判断基础数据类型（null 除外），但是引用数据类型中，除了function 类型以外，其他的也无法判断

    // 如果需要通用检测数据类型，可以采用Object.prototype.toString，调用该方法，统一返回格式“[object Xxx]”的字符串
    console.log(Object.prototype.toString({}));       // "[object Object]"
    console.log(Object.prototype.toString.call({}));  // 同上结果，加上call也ok
    console.log(Object.prototype.toString.call(1));    // "[object Number]"
    console.log(Object.prototype.toString.call('1'));  // "[object String]"
    console.log(Object.prototype.toString.call(true));  // "[object Boolean]"
    console.log(Object.prototype.toString.call(function(){}));  // "[object Function]"
    console.log(Object.prototype.toString.call(null));   //"[object Null]"
    console.log(Object.prototype.toString.call(undefined)); //"[object Undefined]"
    console.log(Object.prototype.toString.call(/123/g));    //"[object RegExp]"
    console.log(Object.prototype.toString.call(new Date())); //"[object Date]"
    console.log(Object.prototype.toString.call([]));       //"[object Array]"
    console.log(Object.prototype.toString.call(document));  //"[object HTMLDocument]"
    console.log(Object.prototype.toString.call(window));   //"[object Window]"

    // 下面就实现一个全局通用的数据类型判断方法
    function getType(obj){
      let type  = typeof obj;
      if (type !== "object") {    // 先进行typeof判断，如果是基础数据类型，直接返回
        return type;
      }
      // 对于typeof返回结果是object的，再进行如下的判断，正则返回结果
      // console.log(Object.prototype.toString(obj).replace(/^\[object (\S+)\]$/, '$1'));
      return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1'); 
    }
    getType([])     // "Array" typeof []是object，因此toString返回
    getType('123')  // "string" typeof 直接返回
    getType(window) // "Window" toString返回
    getType(null)   // "Null"首字母大写，typeof null是object，需toString来判断
    getType(undefined)   // "undefined" typeof 直接返回
    getType()            // "undefined" typeof 直接返回
    getType(function(){}) // "function" typeof能判断，因此首字母小写
    getType(/123/g)      //"RegExp" toString返回


    
  </script>
</body>
</html>